use core::intrinsics::unlikely;
use core::mem::offset_of;
use core::sync::atomic::AtomicUsize;
use core::{intrinsics::likely, mem::size_of};

use cpuindex::this_processor_id;
use irqflags::{local_irq_restore, local_irq_save};
use raw_list::{define_raw_list_head, RawListHead};
use raw_perdata::RawPerdata;
use spinlock::Spinlock;

use crate::addrspace::PfnAddr;
use crate::{error::Result, page::Page, GfpFlags};

pub(crate) const MAX_ORDER: usize = 11;

pub(crate) struct FreeArea {
    pub(crate) free_list: RawListHead<Page>,
    pub(crate) nr_free: usize,
}

pub(crate) struct PerdataPages {
    pub(crate) count: u32,
    pub(crate) high: u32,
    pub(crate) batch: u32,

    pub(crate) lists: RawListHead<Page>,
}

pub(crate) enum ZoneType {
    Dma,
    Normal,
    Max,
}

pub(crate) struct ZoneInner {
    pub(crate) pcp: RawPerdata<PerdataPages>,
    pub(crate) free_area: [FreeArea; MAX_ORDER],
}

pub(crate) struct Zone {
    pub(crate) inner: Spinlock<ZoneInner>,
    zone_start_pfn: usize,
    managed_pages: AtomicUsize,
    name: &'static str,
}

impl Zone {
    fn expand(page: &mut Page, mut low: usize, mut high: usize, area: *mut FreeArea) {
        let mut size = 1 << high;

        let mut current_area;
        unsafe {
            current_area = area.as_mut().unwrap();
        }
        while high > low {
            unsafe {
                current_area = (current_area as *mut FreeArea).sub(1).as_mut().unwrap();
            }
            high -= 1;
            size >>= 1;

            unsafe {
                let mut current_page = (page as *mut Page).add(size).as_mut().unwrap();
                current_page.buddy_list_add(&mut current_area.free_list);
                current_page.set_page_order(high);
            }
            current_area.nr_free += 1;
        }
    }

    fn __rmqueue_smallest(zone_inner: &mut ZoneInner, order: usize) -> Option<*mut Page> {
        for current_order in order..MAX_ORDER {
            let mut area = &mut zone_inner.free_area[current_order];
            let raw_area = area as *mut FreeArea;
            unsafe {
                let page = area.free_list.list_first_entry_or_null_mut();
                if let Some(p) = page {
                    p.buddy_list_del();
                    p.rmv_page_order();
                    area.nr_free -= 1;
                    Zone::expand(p, order, current_order, raw_area);
                    return Some(p as *mut Page);
                }
            }
        }
        None
    }

    fn rmqueue_bulk(
        &mut self,
        order: usize,
        count: usize,
        head: &mut RawListHead<Page>,
        gfp_mask: GfpFlags,
    ) -> u32 {
        let mut alloced = 0;
        let mut lock = self.inner.lock();
        for i in 0..count {
            let page = Zone::__rmqueue_smallest(&mut lock, order);
            let p = page.is_some();
            if let Some(p) = page {
                unsafe {
                    let pp = p.as_mut().unwrap();
                    if unlikely(pp.check_pcp_refill()) {
                        continue;
                    }
                    pp.buddy_list_add(head);
                    alloced += 1;
                }
            } else {
                break;
            }
        }
        alloced
    }

    fn __rmqueue_pcplist(
        &mut self,
        gfp_mask: GfpFlags,
        pcp_raw: *mut PerdataPages,
        head_raw: *mut RawListHead<Page>,
    ) -> Option<*mut Page> {
        let mut pcp;
        let mut head;
        unsafe {
            pcp = pcp_raw.as_mut().unwrap();
            head = head_raw.as_mut().unwrap();
        }
        let mut page;
        loop {
            unsafe {
                if head.list_empty() {
                    pcp.count += self.rmqueue_bulk(0, pcp.batch as usize, head, gfp_mask);
                    if unlikely(head.list_empty()) {
                        return None;
                    }
                }

                page = head.list_first_entry_or_null_mut().unwrap();
                page.buddy_list_del();
                pcp.count -= 1;
                if page.check_new_pcp() {
                    break;
                }
            }
        }
        Some(page as *mut Page)
    }

    fn rmqueue_pcplist(&mut self, order: usize, gfp_mask: GfpFlags) -> Option<*mut Page> {
        let mut pcp;
        let mut head;

        // 对本地数据访问我们只需要禁用中断即可, 不用浪费时间在锁上
        {
            let mut lock = self.inner.lock_irq_save();
            let mut this_pcp = lock.pcp.get_mut(this_processor_id());
            head = &mut this_pcp.lists as *mut RawListHead<Page>;
            pcp = this_pcp as *mut PerdataPages;
        }
        let flags = local_irq_save();
        let page = self.__rmqueue_pcplist(gfp_mask, pcp, head);
        local_irq_restore(flags);
        page
    }

    #[inline(always)]
    pub(crate) fn rmqueue(&mut self, order: usize, gfp_mask: GfpFlags) -> Option<*mut Page> {
        if likely(order == 0) {
            return self.rmqueue_pcplist(order, gfp_mask);
        }

        let mut lock = self.inner.lock_irq_save();
        let page = Zone::__rmqueue_smallest(&mut lock, order);
        if let Some(p) = page {
            unsafe {
                p.as_mut().unwrap().check_new_pages(order).unwrap();
            }
            return Some(p);
        }
        None
    }

    pub(crate) fn free_pcppages_bulk(&mut self, mut count: usize, pcp_raw: *mut PerdataPages) {
        define_raw_list_head!(head, Page, ty.Head.0.lru);

        let mut batch_free = 0;
        let mut prefetch_nr = 0;
        let mut pcp;
        unsafe {
            pcp = pcp_raw.as_mut().unwrap();
        }
        while count != 0 {
            batch_free += 1;
            let list = &mut pcp.lists;
            loop {
                let page;
                unsafe {
                    page = list.list_last_entry_mut();
                }
                page.buddy_list_del();
                pcp.count -= 1;

                page.free_pages_check().unwrap();

                page.buddy_list_add_tail(&mut head);

                if prefetch_nr < pcp.batch {
                    page.prefetch_buddy();
                }
                prefetch_nr += 1;
                count -= 1;
                batch_free -= 1;
                unsafe {
                    if count == 0 || batch_free == 0 || list.list_empty() {
                        break;
                    }
                }
            }
        }

        let mut lock = self.inner.lock();
        for p in head.iter_mut() {
            p.__free_one_page(&mut lock, p.page_to_pfn(), 0);
        }
    }

    pub(crate) fn drain_pages_zone(&mut self, cpu: usize) -> u32 {
        let mut has_page = 0;
        // 处理本地 cpu 数据不需要锁, 禁用中断即可
        let raw_pcp;
        {
            let mut lock = self.inner.lock_irq_save();
            raw_pcp = lock.pcp.get_mut(cpu) as *mut PerdataPages;
        }
        let flags = local_irq_save();
        unsafe {
            let pcp = raw_pcp.as_mut().unwrap();
            if pcp.count != 0 {
                has_page = 1;
                self.free_pcppages_bulk(pcp.count as usize, raw_pcp);
            }
        }

        local_irq_restore(flags);
        has_page
    }

    pub(crate) fn free_one_page(&mut self, page: &mut Page, pfn: PfnAddr, order: usize) {
        let mut lock = self.inner.lock();
        page.__free_one_page(&mut lock, pfn, order);
    }
}
